#include "drv_uart.h"

/*----------[printf]Support arm compiler 6, Must USE MircoLIB, uart.h #include "stdio.h"----------*/
#define Printf_UART USART1
__asm (".global __use_no_semihosting\n\t");
void _sys_exit(int x) 
{
  x = x;
}
#if defined ( __GNUC__ ) && !defined (__clang__) 
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif 
PUTCHAR_PROTOTYPE
{
	while(LL_USART_IsActiveFlag_TXE(Printf_UART) != 1);
	LL_USART_TransmitData8(Printf_UART, ch);
	return ch;
}

#define USART1_PORT         USART1
#define USART1_TX_PIN       LL_GPIO_PIN_2
#define USART1_RX_PIN       LL_GPIO_PIN_3

#define USART2_PORT         USART2
#define USART2_TX_PIN       LL_GPIO_PIN_0
#define USART2_RX_PIN       LL_GPIO_PIN_1

#define UART_CACHE_SIZE     (0x200)
#define UART_UNINITIALIZED  (0x00)
#define UART_INITIALIZED    (0x01)

typedef struct {
    unsigned char   buff[UART_CACHE_SIZE];
    unsigned short  length;
    unsigned short  offset;
} drv_uart_cache_t;

typedef struct {
    drv_uart_cache_t    rx[UART_NUM_MAX];
    unsigned char       init[UART_NUM_MAX];
} drv_uart_priv_t;

volatile static drv_uart_priv_t uart_priv = {0x00};
static void drv_uart_init_uart1(unsigned int baudrate)
{
    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_USART1);

    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin         = USART1_TX_PIN;
    GPIO_InitStruct.Mode        = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed       = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType  = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull        = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate   = LL_GPIO_AF1_USART1;
    LL_GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    GPIO_InitStruct.Pin         = USART1_RX_PIN;
    GPIO_InitStruct.Alternate   = LL_GPIO_AF1_USART1;
    LL_GPIO_Init(GPIOA,&GPIO_InitStruct);

    NVIC_SetPriority(USART1_IRQn,1);
    NVIC_EnableIRQ(USART1_IRQn);

    LL_USART_InitTypeDef USART_InitStruct = {0};
    USART_InitStruct.BaudRate               = baudrate;
    USART_InitStruct.DataWidth              = LL_USART_DATAWIDTH_8B;
    USART_InitStruct.StopBits               = LL_USART_STOPBITS_1;
    USART_InitStruct.Parity                 = LL_USART_PARITY_NONE;
    USART_InitStruct.TransferDirection      = LL_USART_DIRECTION_TX_RX;
    USART_InitStruct.HardwareFlowControl    = LL_USART_HWCONTROL_NONE;
    USART_InitStruct.OverSampling           = LL_USART_OVERSAMPLING_16;
    LL_USART_Init(USART1, &USART_InitStruct);
    LL_USART_ConfigAsyncMode(USART1);
    LL_USART_Enable(USART1);
    LL_USART_EnableIT_PE(USART1);
    LL_USART_EnableIT_ERROR(USART1);
    LL_USART_EnableIT_RXNE(USART1);
}

static void drv_uart_init_uart2(unsigned int baudrate)
{
    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);

    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin         = USART2_TX_PIN;
    GPIO_InitStruct.Mode        = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed       = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType  = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull        = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate   = LL_GPIO_AF9_USART2;
    LL_GPIO_Init(GPIOA,&GPIO_InitStruct);

    GPIO_InitStruct.Pin         = USART2_RX_PIN;
    GPIO_InitStruct.Alternate   = LL_GPIO_AF9_USART2;
    LL_GPIO_Init(GPIOA,&GPIO_InitStruct);
        
    NVIC_SetPriority(USART2_IRQn,2);
    NVIC_EnableIRQ(USART2_IRQn);

    LL_USART_InitTypeDef USART_InitStruct = {0};
    USART_InitStruct.BaudRate               = baudrate;
    USART_InitStruct.DataWidth              = LL_USART_DATAWIDTH_8B;
    USART_InitStruct.StopBits               = LL_USART_STOPBITS_1;
    USART_InitStruct.Parity                 = LL_USART_PARITY_NONE;
    USART_InitStruct.TransferDirection      = LL_USART_DIRECTION_TX_RX;
    USART_InitStruct.HardwareFlowControl    = LL_USART_HWCONTROL_NONE;
    USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
    LL_USART_Init(USART2, &USART_InitStruct);
    
    LL_USART_ConfigAsyncMode(USART2);
    LL_USART_Enable(USART2);
    LL_USART_EnableIT_PE(USART2);
    LL_USART_EnableIT_ERROR(USART2);
    LL_USART_EnableIT_RXNE(USART2);
}

void drv_uart_init(drv_uart_port_t huart, unsigned int baudreate)
{
    ret_if_true(uart_priv.init[huart] == UART_INITIALIZED)

    if (huart == UART_USART1) {
        drv_uart_init_uart1(baudreate);
    } else if (huart == UART_USART2) {
        drv_uart_init_uart2(baudreate);
    }
    uart_priv.init[huart] = UART_INITIALIZED;
}

void drv_uart_deinit(drv_uart_port_t huart)
{
    ret_if_true(uart_priv.init[huart] == UART_UNINITIALIZED)
    if (huart == UART_USART1) {
        LL_USART_DeInit(USART1);
    } else if (huart == UART_USART2) {
        LL_USART_DeInit(USART2);
    }
    uart_priv.init[huart] = UART_UNINITIALIZED;
}

void drv_uart_irq_handler(drv_uart_port_t huart)
{
    USART_TypeDef *USARTx = (huart == UART_USART1) ? (USART1) : (USART2);

    unsigned int errorflags = (LL_USART_IsActiveFlag_PE(USARTx) | LL_USART_IsActiveFlag_FE(USARTx) 
                            | LL_USART_IsActiveFlag_ORE(USARTx) | LL_USART_IsActiveFlag_NE(USARTx));
    if (errorflags == RESET) {
        if ((LL_USART_IsActiveFlag_RXNE(USARTx) != RESET) && (LL_USART_IsEnabledIT_RXNE(USARTx) != RESET)) {
            unsigned char dat = LL_USART_ReceiveData8(USARTx);
            uart_priv.rx[huart].buff[uart_priv.rx[huart].length++] = dat;
            if (uart_priv.rx[huart].length >= UART_CACHE_SIZE) {
                uart_priv.rx[huart].length = 0x00;
            }
            return;
        }
    }

    if (errorflags != RESET) {
        LL_USART_ClearFlag_ORE(USARTx);
        LL_USART_ClearFlag_NE(USARTx);
        LL_USART_ClearFlag_FE(USARTx);
        LL_USART_ClearFlag_PE(USARTx);
    }
}

unsigned char drv_uart_rx_is_empty(drv_uart_port_t huart)
{
    if (uart_priv.rx[huart].offset == uart_priv.rx[huart].length) {
        return 0x01;
    }

    return 0x00;
}

void drv_uart_write_data(drv_uart_port_t huart, const unsigned char *buffer, unsigned short length)
{
    ret_if_true(uart_priv.init[huart] == UART_UNINITIALIZED);

    USART_TypeDef *USARTx = (huart == UART_USART1) ? (USART1) : (USART2);

    unsigned short _txLen = 0;
    while (_txLen < length) {
        while(LL_USART_IsActiveFlag_TXE(USARTx) != 1);
        LL_USART_TransmitData8(USARTx, buffer[_txLen]);
        _txLen++;
    }
}

unsigned char drv_uart_read_byte(drv_uart_port_t huart)
{
    ret_val_if_true(uart_priv.init[huart] == UART_UNINITIALIZED, 0x00);
    ret_val_if_true(0x01 == drv_uart_rx_is_empty(huart), 0x00);

    unsigned char dat = uart_priv.rx[huart].buff[uart_priv.rx[huart].offset++];
    if (uart_priv.rx[huart].offset >= UART_CACHE_SIZE) {
        uart_priv.rx[huart].offset = 0x00;
    }

    return dat;
}

